
/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTDAILERN OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTDAILERN WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: hellowrold.cpp
 *
 * Purpose: transport dialer and listener base test(say hello).
 * - client: connect to server and send a message "hello, i'm a client"
 * - server: bind as a server and send response when recv a message from client.
 *
 * Developer:
 *   wen.gu , 2023-07-20
 *
 * TODO:
 *
 ***************************************************************************/
#include "xbus/transport/xtransport.h"

#include <string>

#include "xbus/osal/xthread.h"


using namespace xbus::transport;
using namespace xbus::core;
using namespace xbus::osal;

#define UDS_SEND_PRIORITY 0
#define UDS_RECV_PRIORITY 0
#define UDS_SEND_QUEUE_MAX_SIZE 0u
#define UDS_RECV_QUEUE_MAX_SIZE 0u

#define UDS_CLIENT_REQUEST_MSG_TYPE 1
#define UDS_SERVER_RESPONSE_MSG_TYPE 2


static void ClientTest(const std::string& url) {
    auto res = xbus::transport::XTransport::get().createDialer(url);
    if (!res) {
        printf("create client(for url: %s) failed(%d)\n", url.c_str(), static_cast<int32_t>(res.error()));
        return ;
    }

    auto client = res.value();
    auto ret = client->initialize(UDS_SEND_PRIORITY, UDS_RECV_PRIORITY, UDS_SEND_QUEUE_MAX_SIZE, UDS_RECV_QUEUE_MAX_SIZE);

    if (XErrC::OK != ret) {
        printf("initialize client(for url: %s) failed(%d)\n", url.c_str(), static_cast<int32_t>(res.error()));
        return ;
    }

    std::string hello = "hello, i'm a" + client->trans_protocol() + "client!!, who is that?";

    printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
    bool is_connected_to_server = false;
    client->set_status_handler([&](bool is_connected){
        printf("[%s.%d]====>: connect status to srv: %s\n", __FUNCTION__, __LINE__, is_connected ? "connected" : "disconnected");
        is_connected_to_server = is_connected;
    });

    printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
    ret = client->start();

    printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);

    if (ret == XErrC::OK) {

        printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
        while (!is_connected_to_server) {
            SleepMs(5);
        }

        printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
        ret = client->send(UDS_CLIENT_REQUEST_MSG_TYPE, hello.size() + 1, reinterpret_cast<const uint8_t*>(hello.data()));
        printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
        if (ret != XErrC::OK) {
            printf("client(%s) send request message(%s) failed(%d)\n", url.c_str(), hello.c_str(), static_cast<int>(ret));
        }

        printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);

        while (1) {
            //printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
            auto res = client->recv(20);
            //printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);

            if (res.hasValue()) {
                auto msg_ptr = res.value();
                printf("client(%s) received a message(%s) type: %d\n", url.c_str(), reinterpret_cast<const char*>(msg_ptr->payload()), msg_ptr->type());
                break;
            }
        }
        printf("[%s.%d]====>:\n", __FUNCTION__, __LINE__);
        ret = client->stop();
        printf("[%s.%d]====>:ret(%d)\n", __FUNCTION__, __LINE__, static_cast<int>(ret));


    } else {
        printf("start client(%s) failed(%d)\n", url.c_str(), static_cast<int>(ret));
    }

} 


static void ServerTest(const std::string& url) {
    auto res = xbus::transport::XTransport::get().createListener(url);
    if (!res) {
        printf("create server(for url: %s) failed(%d)\n", url.c_str(), static_cast<int32_t>(res.error()));
        return ;
    }

    auto server = res.value();
    auto ret = server->initialize(UDS_SEND_PRIORITY, UDS_RECV_PRIORITY, UDS_SEND_QUEUE_MAX_SIZE, UDS_RECV_QUEUE_MAX_SIZE);

    if (XErrC::OK != ret) {
        printf("initialize server(for url: %s) failed(%d)\n", url.c_str(), static_cast<int32_t>(res.error()));
        return ;
    }

    server->set_status_handler([url](XComId client_id, bool is_connected){
        printf("a client(id:%ld) %s to current server(%s)\n", client_id, is_connected ? "connected": "disconnected", url.c_str());
    });

    ret = server->start();
    if (XErrC::OK != ret) {
        printf("start server(%s) failed(%d)\n", url.c_str(), static_cast<int>(ret));
        return ;
    }

    std::string response = "hello, i'm a " + server->trans_protocol() + " server, this is a response message";

    while (1) {
        auto res = server->recv(20);

        if (res.hasValue()) {
            auto msg_ptr = res.value();
            printf("server(%s) received a message(%s), type: %d\n", url.c_str(), reinterpret_cast<const char*>(msg_ptr->payload()), msg_ptr->type());
            ret = server->send(msg_ptr->src_com_id(), UDS_SERVER_RESPONSE_MSG_TYPE, response.size() + 1, reinterpret_cast<const uint8_t*>(response.data()));

            if (XErrC::OK != ret) {
                printf("server(%s) send response message(%s) failed(%d)\n", url.c_str(), response.c_str(), static_cast<int>(ret));
            }
        }
    }

}

void ShowUsage() {
    printf(
        "hellouds role xxx\n\n"
        "role: \n"
        "    server     as server side\n"
        "    client     as client side\n\n"
        "xxx: the url for uds server and client, the syntax is: \"proto://addr[:port]\" \n"
        "               proto: the transport layer proctocol, e.g. tcp, udp, uds(unix domain socket) \n"
        "               addr:  the url address \n"
        "               [:port] :  option segment, only use for like tcp, udp and so on. \n"
    );
}


int main(int argc, char* argv[]) {

    if (argc < 3) {
        ShowUsage();
        return 0;
    }

    std::string role = argv[1];
    std::string url = argv[2];

    if (role == "server") {
        ServerTest(url.c_str());
    } else if (role == "client") {
        ClientTest(url);
    } else {
        ShowUsage();
    }

    return 0;
}





